/**
 * BudgetBuoy
 * 
 * Simplify your personal expense management
 * 
 * @team cs2103jan13-w15-3j
 */

//--------------------------------------------------------------

/**
 * Tools Class
 * 
 * Description: 
 * 
 * 
 * @package   cli.tools
 * @author    Lai Yong Quan (A0080909Y)
 * @edited by Tan Si Ling (A0085314J)
 */

//To do: check date for future input
package cli.tools;

import java.text.DateFormat;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cli.CLI;
import cli.menu.*;
import shared.*;

public class Tools{


	private static  int entriesPerPage;
	private static  DateFormat dateFormat;
	public  Action action;
	private char[] HOTKEYS;
	private static CLI cli;
	
	public Tools(CLI cli){
	this.cli = cli;
	entriesPerPage = cli.getEntriesPerPage();
	dateFormat = cli.getDateFormat();
	action = cli.getAction();
	HOTKEYS = cli.getHotkeys();
	}

	public boolean isHotKey(char input){

		for(char c: HOTKEYS)
			if(input == c)
				return true;
		return false;
	}
	
	//method to see if the first date is before the second date
	//int[] is to be in this order: [day][month][year]
	//returns true if first <= second
	public boolean dateIsBefore(int[] first, int[] second) throws Exception{ 
			
		if(first.length != 3 || second.length != 3){
			cli.print("Error : invalid dates provided");
			throw new Exception("Error : invalid dates provided");
		}
		
	
		//compares the year first
		if(second[2] < first[2]){
			return false;
		}
		if(second[2] > first[2]){
			return true;
		}
		
		//if year is the same
		//check month
		if(second[1] < first[1]){
			return false;
		}
		if(second[1] > first[1]){
			return true;
		}
		//if month is the same
		//check day
		if(second[0] < first[0]){
			return false;
		}
		if(second[0] >= first[0]){
			return true;
		}
		return false;
	}

	public String formatDate(Date date){

		return dateFormat.format(date);

	}

	public int[] initDate() {
		Date currentDate = new Date();
		String date = dateFormat.format(currentDate);
		String[] temp = date.split("\\.");
		int[] intDate = new int[3];
		intDate[0] = Integer.valueOf(temp[0]);
		intDate[1] = Integer.valueOf(temp[1]);
		intDate[2] = Integer.valueOf(temp[2]);
		
		return intDate;
	}

	public int[] processDate(String date) throws Exception {

		//remove possible spaces
		date = date.trim();
		
		int[] intDate = new int[3];
		// checks if it is a date
		dateFormat.parse(date);

		String[] temp = date.split("\\.");

		
		
		//should be unnecessary
		/*
		if (temp.length != 3){
			cli.println("Incorrect date format");
			invalidDate = true;
		}
		 */


		//process date and return it if it is valid

		for (int i = 0; i < 3; i++) {
			
			intDate[i] = Integer.parseInt(temp[i].trim());
			
		}
		
		return intDate;

	}
	

public int Money2int(String input){
		
		input = input.trim();
	
		if(input.split("\\.").length > 2){
			cli.println("Invalid money format");
		}
		
		Pattern moneyPattern = Pattern.compile("\\d*\\.{0,1}\\d{0,2}");
		Matcher money = moneyPattern.matcher(input);
		String price = null;
		
			money.find();
			price = money.group();
	//	System.out.println(price + "amount found");
		
		Scanner scanner = new Scanner(price);
		double value;
		try{
			//price may not be money as intended
		value = scanner.nextDouble();
		scanner.close();
		}
		catch(Exception e){
			scanner.close();
			return 0;
		}
		scanner.close();
		if (value < 0){
		cli.println("Invalid amount given");
			return 0;
		}
		return (int) (value * 100.00);
	}
	
	public static boolean checkPeriod(String input){
		input = input.trim();
		if(input.equalsIgnoreCase("day") || input.equalsIgnoreCase("month") || input.equalsIgnoreCase("year"))
			return true;
		return false;
	}

	//helper function to add category from either manage or selection from list
	protected Category appendCategory() {

	
		
		String input = null;
		
		while(input == null || input.length() == 0){
			cli.println("Enter the name for the new category: ");
			input = cli.nextLine();
		}
			
		Category category;
		try{
			category = cli.getApi().addCategory(input);
		}
		catch(Exception e){
		
			cli.println("Failed to add " + input + "as a category");
			return null;
		}
		// Si Ling: **Keeping Track of the Actions DONE**
		action.addToUndoStack(null,category,Process.ADDCATEGORIES, null);
		// **END**
	
		return category;

	}

	//helper function to add Payment Mode (from selection or manage screen
	// takes in a string as the name of the new payment mode and returns the newly created mode
	protected Mode appendPaymentMode() {	

		String input = null;
		
		while(input == null || input.length() == 0){
		cli.println("Enter the name for the new Payment Mode: ");
		input = cli.nextLine();
		}
		
		Mode paymentMode;

		try{
			paymentMode= cli.getApi().addMode(input);
		}
		catch(Exception e){
			cli.println("Failed to add " + input + "as a Payment Mode");
			return null;
		}

		// Si Ling: **Keeping Track of the Actions DONE**
		action.addToUndoStack(null, paymentMode,
				Process.ADDPAYMENTMODES,null);
		// **END**

		return paymentMode;

	}

	public static  void printFromList(List<Object> list, int pageIndexToPrint,
			String state, boolean append) {

		int i;
		
		// indexes are 0 based start : X0, end: X9 where X is another number
		int startIndex = (pageIndexToPrint - 1) * 10;
		int endIndex = (pageIndexToPrint * 10) - 1;

		// checks for boundary
		if (endIndex > list.size())
			endIndex = list.size() - 1;

		int currentPage = (startIndex / entriesPerPage) + 1;
		int totalPages = (list.size() / entriesPerPage) + 1;

		// if there are fewer categories than the "page length"
		if (list.size() <= entriesPerPage) {
			for (i = startIndex; i < list.size(); i++) {
				if (list.get(i) == null)
					break;
				//cli.println(i+1 + ". " + list.get(i).toString());
				String temp = String.format("%3d. ", i+1);
				cli.println(temp + list.get(i).toString());
			}
			if (append)
				cli.println("  a. Add " + state);
		}

		else {

			for (i = startIndex; i <= endIndex; i++) {
				if (list.get(i) == null)
					break;
				cli.println(i+1 + ". " + list.get(i).toString());
			}

			// bottomBar : [previous][current page #/ total page #][next]
			String[] bottomBar = new String[3];

			bottomBar[0] = "[p]: previous";
			bottomBar[1] = "  Page [ " + currentPage + " / " + totalPages
					+ " ]  ";
			bottomBar[2] = "[n]: next";

			if (pageIndexToPrint == 1)
				bottomBar[0] = "             ";
			if (pageIndexToPrint == totalPages)
				bottomBar[2] = "         ";

			cli.print(bottomBar[0] + bottomBar[1] + bottomBar[2]);

			if (append)
				cli.println("a. Add " + state.toString());
		}

	}

	// method to facilitate user's choice selection from a list
	// results are paginated if there are too many options
	// see entriesPerPage for the number of items per page
	// returns the selected object if valid
	// returns null or an Instruction object for back and mainMenu respectively
	public Object selectFromList(List wantedList, String state, String... header){

		if(wantedList.isEmpty()){
			cli.println("There are no entries to choose from");
			//signal back
			return null;
		}

		//temporary fix
		//Loan Payment Category is meant to be obscured away from the user
		if(wantedList.get(0).getClass() == Category.class)
			wantedList.remove(1);
		
		
		// currentPage is the current viewing page
		int currentPage = 1;
		// whether to offer an add option or not
		boolean append = false;
		boolean selectionNotMade = true;
		State nextState;
		// we want to allow users to be able to append entries when viewing
		// certain lists
		// such as payment modes

		if (state.equalsIgnoreCase("Payment Mode") || state.equalsIgnoreCase("Category"))
			append = true;

		int totalPages = (wantedList.size() / entriesPerPage) + 1;
		
		String input;
		char charInput;

		// Loop while users have not made a choice from the list
		while (selectionNotMade) {

			// output to screen
			if(header != null || header.length > 0)
			cli.print(header);
			printFromList(wantedList, currentPage, state, append);
			input = cli.nextLine();
			cli.clearScreen();
			if( input == null || input.length() == 0 )
				continue;
			charInput = input.charAt(0);
			switch (charInput) {
			case 'a':
			case 'A':
				if (state.equalsIgnoreCase("Payment Mode")) {
					// if adding payment mode from entering Expenses, the added
					// payment mode
					// will be selected by default
				
					return appendPaymentMode();

				}
				else if(state.equalsIgnoreCase("Category")){
					
					return appendCategory();

				}
				break;
			case 'b':
			case 'B':
				// returns null to indicate a back operation, function that
				// calls selectFromList will
				// subsequently return itself to main()
				return null;
			case 'u':
			case 'U':
				undo();
				break;
			case 'r':
			case 'R':
				redo();
				break;
			case 'h':
			case 'H':
				// returns instruction to mainMenu if home is used
				nextState = State.MAINMENU;
				return nextState;
			case 'N':
			case 'n':
				// page flipping within limits
				if (currentPage < totalPages) {
					currentPage++;
				}
				break;
			case 'P':
			case 'p':
				// page flipping within limits
				if (currentPage > 1) {
					currentPage--;
				}
				break;

			default: {
				// since it is not a page flipping action or special f(n) -
				// check if it is a
				// valid input
				int choice;
				try{
				choice = Integer.parseInt(input);
				if (choice <= wantedList.size() && choice > 0) {
					return wantedList.get(choice-1);
				} else {
					cli.println("Invalid input: Index entered is out of bound");
				}
				}
				//input is not a number
				catch(Exception e){
					cli.println("Invalid input: Number expected but received +'" + input +"'");
				}
			}

			}
		}
	
		return null;
	}

	public void undo() {
		try{
			action.undo();
		}
		catch(Exception e){
//			cli.println("--Error--");
//			cli.println("Unable to undo previous operation");
		}
	}

	public void redo() {
		try{
			action.redo();
		}
		catch(Exception e){
//			cli.println("--Error--");
//			cli.println("Unable to undo previous operation");
		}
	}

	// assisting function
	public String formatString(String s, int newLength) {
		String newString = new String(s);
		int blankToAdd = newLength - s.length();
		if (blankToAdd > 0) {
			while (blankToAdd-- > 0) {
				newString = newString + " ";
			}
			return newString;
		} else {
			return newString.substring(0, newLength - 3) + "...";
		}
	}

	public String printLine(String character, int len) {
		String line = "";
		for (int i = 0; i < len; i++) {
			line = line + character;
		}
		return line;
	}

	
}
